﻿/**
 * This library is a thing to realize D2L and DI and AOP on Flex2(ActionScript3).
 * Copyright (C) 2007 Takamasa Seki
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 * 
 * Contributor : Takamasa Seki(takanafu@gmail.com)
 *               http://www.takanafu.com/
 */

package com.takanafu.haruframework.d2l.invoker{
	import com.takanafu.core.haru_internal;
	import com.takanafu.haruframework.aop.AfterReturningAdvice;
	import com.takanafu.haruframework.aop.MethodBeforeAdvice;
	import com.takanafu.haruframework.aop.ThrowsAdvice;
	import com.takanafu.haruframework.aop.reflect.ClassImpl;
	import com.takanafu.haruframework.aop.reflect.MethodImpl;
	import com.takanafu.haruframework.aop.support.AbstractPointcutAdvisor;
	import com.takanafu.haruframework.aopalliance.reflect.Method;
	
	import flash.events.Event;
	import flash.events.EventDispatcher;
	
	import mx.collections.ArrayCollection;
	import mx.core.Application;
	import mx.core.UIComponent;
	import mx.core.UIComponentDescriptor;
	import com.takanafu.haruframework.beans.BeanConstant;
	
	use namespace haru_internal;
	
	public class InvocationProxyImpl implements IInvocationProxy{
		private static var instance:IInvocationProxy;
		private var delegateClassMap:Object;
		private var pointcutAdvisorMap:Object;
		
		private var beanMap:Object;
		private var beanSingletonMap:Object;
		
		public function InvocationProxyImpl(){
			super();
		}
		
		public static function getInstance():IInvocationProxy{
	        if(!instance){
	            instance = new InvocationProxyImpl();
	        }
	
	        return instance;
	    }
		
		
		public function registBean(bean:XML,clazz:Class):void{
			var id:String = bean.attribute("id").toString();
			
			if(clazz == null){
				//throw new Error(id+" Class が null です。");
				return;
			}
			if(beanMap == null){
				beanMap = new Object();
			}
			beanMap[id] = new Object();
			beanMap[id]["info"] = bean;
			beanMap[id]["class"] = clazz;
			beanMap[id]["classImpl"] = null;
		}
		
		public function addInvocationHandler(obj:Application,prop:XML,info:XML,clazz:Class,method:String,evt:String):void{
			registerDelegateClass(obj.id,prop,info,method,clazz);
			obj.addEventListener(evt,delegateHandler);
		}

		public function registInvocationHandler(obj:UIComponentDescriptor,prop:XML,info:XML,clazz:Class,method:String,evt:String):UIComponentDescriptor{
			if(obj.events == null){
				obj.events = new Object();
			}
			obj.events[evt] = "relayInvocationHandler";
			registerDelegateClass(obj.id,prop,info,method,clazz);
			
			return obj;
		}
		
		public function relayInvocationHandler(event:*):void{
			delegateHandler(event);
		}
		
		private function delegateHandler(event:Event):void{
			var id:String = event.target.hasOwnProperty("id") == true ? event.target["id"] : null;
			
			if(id != null){
				var obj:Object = getDelegateClass(id,event.type);
				var invoker:AbstractInvoker = obj != null ? InvokerFactory.getInvoker(obj) : null;
				
				if(invoker != null){
					invoker.invoke(event);
				}
			}
		}
		
		public function registPointcutAdvisor():void{
			for each(var prop:Object in beanMap){
				var clsName:String = prop.info.attribute("class").toString();
			
				if(clsName == "com.takanafu.haruframework.aop.support.RegexpMethodPointcutAdvisor"){
					var id:String = prop.info.attribute("id").toString();
					var ary:Array = String(prop.info.property.(@name=="patterns").value).split(",");
					var ref:String = String(prop.info.property.(@name=="advice").ref.@local);

					if(pointcutAdvisorMap == null){
			            pointcutAdvisorMap = {};
			        }
			        
			        if(pointcutAdvisorMap[id] == null){
			        	pointcutAdvisorMap[id] = {};
			        }
			        
			        pointcutAdvisorMap[id]["type"] = "RegexpMethod";
			        pointcutAdvisorMap[id]["advice"] = new Object();
			        
			        var clsImpl:Object = getBean(ref);
			        
			        if(clsImpl != null){
				        pointcutAdvisorMap[id]["advice"]["bean"] = clsImpl;
						if(prop.info.hasOwnProperty("@priority") == true){
							pointcutAdvisorMap[id]["advice"]["priority"] = prop.info.@priority;
						}else{
							pointcutAdvisorMap[id]["advice"]["priority"] = BeanConstant.PRIORITY_DEFAULT;
						}
				        pointcutAdvisorMap[id]["patterns"] = new ArrayCollection(ary);
					}
				}
			}
		}
		
		private function registerDelegateClass(uid:String,prop:XML,info:XML,method:String,delegateClass:Class):Object{
	        if(delegateClassMap == null){
	            delegateClassMap = {};
	        }
	        
	        if(delegateClassMap[uid] == null){
	        	delegateClassMap[uid] = {};
	        }
	        
			delegateClassMap[uid][prop.@id] = new Object();
			var clsImpl:ClassImpl = getBean(info.@id) as ClassImpl;
			var cls:Object = clsImpl.getClass();
			
			if(cls.hasOwnProperty(method) == false){
				//throw new Error(method+" メソッドが見当たりません");
				return null;
			}
			var mtd:Function = cls[method];
			var mtdImpl:MethodImpl = new MethodImpl(mtd,method);
			
			delegateClassMap[uid][prop.@id]["class"] = clsImpl;
			delegateClassMap[uid][prop.@id]["method"] = mtdImpl;
			delegateClassMap[uid][prop.@id]["interceptorNames"] = null;
			for each(var p:XML in info.property){
				if(p.@name == "interceptorNames"){
					var ary:Array = new Array();
					
					for each(var val:XML in info.property.list.value){
						ary.push(val.toString());
					}
					delegateClassMap[uid][prop.@id]["interceptorNames"] = ary;
					break;
				}
			}
			
			return delegateClassMap[uid];
	    }
	    
	    public function getDelegateClassMap():Object{
	    	return this.delegateClassMap;
	    }
	    
	    public function getPointcutAdvisorMap():Object{
	    	return this.pointcutAdvisorMap;
	    }
	    
	    public function getBean(id:String,withInfo:Boolean=false):Object{
			var ret:Object = null;
			
			// beanMap が初期化されない場合の不整合でエラーになるのを回避 20070817
	    	if(beanMap != null && beanMap[id] != null){
	    		var info:XML = beanMap[id]["info"];
	    		var isSingleton:Boolean = true;
	    		
	    		if(info.hasOwnProperty("@singleton") == true){
	    			if(info.@singleton == "false" || info.@singleton == false){
	    				isSingleton = false;
	    			}
	    		}
				
				if(isSingleton == true){
					if(beanMap[id]["classImpl"] == null){
						if(beanMap[id]["class"] != null){
							beanMap[id]["classImpl"] = new ClassImpl(new beanMap[id]["class"]());
						}else{
							//throw new Error("Class が見当たりません。["+id+"]");
				    		return null;
						}
					}
					ret = beanMap[id];
				}else{
					ret = new Object();
					ret["info"] = info;
					ret["class"] = beanMap[id]["class"];
					ret["classImpl"] = new ClassImpl(new beanMap[id]["class"]());
				}
	    	}
	    	
	    	if(withInfo == true){
	    		return ret;
	    	}
	    	
	    	if(ret == null){
	    		//throw new Error("Bean が見当たりません。["+id+"]");
	    		return null;
	    	}
	    	return ret["classImpl"];
	    }
	    
	    haru_internal function getDelegateClass(uid:String,evt:String):Object{
	        if(delegateClassMap[uid] != null){
				return delegateClassMap[uid][evt] as Object;
	        }
	        
	        return null;
	    }
	}
}